package com.bone.config;

import com.bone.common.constant.Dict;
import com.bone.common.exception.BoneException;
import com.bone.common.util.*;
import com.bone.modules.system.entity.SysUser;
import com.bone.modules.system.service.ISysUserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

/**
 * @author :
 * @date : 2020/12/7 13:19
 * Description :
 */
@Slf4j
@Component
public class ShiroRealm extends AuthorizingRealm {

    @Autowired
    private RedisUtils redisUtil;

    @Autowired
    @Lazy
    private ISysUserService sysUserService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SysUser user = (SysUser) principalCollection.getPrimaryPrincipal();
        if (BoneUtils.isEmpty(user)) {
            throw new BoneException("请重新登录！");
        }
        log.info("鉴权开始！——————————————————————————————————");
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        // 加入角色信息 - 权限
        log.info("——————————————————————————————————鉴权结束！");
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String token = (String) authenticationToken.getCredentials();
        if (BoneUtils.isEmpty(token)) {
            log.info("—————身份认证失败——————IP地址:  " + BoneUtils.getIpAddressByRequest(SpringContextUtils.getHttpServletRequest()));
            throw new AuthenticationException("token为空!");
        }
        return new SimpleAuthenticationInfo(this.checkUserTokenIsEffect(token), token, getName());
    }

    /**
     * 校验token的有效性
     *
     * @param token 携带用户信息 以及生命周期
     */
    private SysUser checkUserTokenIsEffect(String token) throws AuthenticationException {
        String userId = JwtUtil.getUserId(token);
        if (BoneUtils.isEmpty(userId)) {
            throw new AuthenticationException("token非法无效!");
        }

        SysUser sysUser;
        String userKey = Dict.PREFIX_USER + userId;
        if (redisUtil.hasKey(userKey)) {
            sysUser = (SysUser) redisUtil.get(userKey);
            log.info("本次用户认证用户信息从缓存中拿去>>>>" + userId);
        } else {
            // 加入缓存
            sysUser = sysUserService.getById(userId);
            redisUtil.set(userKey, sysUser, Dict.REFRESH_TOKEN_TIME);
        }
        if (BoneUtils.isEmpty(sysUser)) {
            throw new AuthenticationException("用户不存在!");
        }
        if (!sysUser.getStatus().equals(Dict.USER_STATUS_OK)) {
            throw new AuthenticationException("当前用户已被冻结!");
        }
        // 满足一下任意一个条件 表示token失效
        // 1- 当前token过期
        // 2- 当前token中的用户 在缓存中不存在Refresh_token
        // 3- 当前token中的时间戳 与缓存中的Refresh_token不一致
        String key = Dict.PREFIX_SHIRO_REFRESH_TOKEN + userId;
        String currentTimeMillis = JwtUtil.getClaim(token, Dict.CURRENT_TIME_MILLIS);
        if (!JwtUtil.verify(token, userId, sysUser.getPassword()) ||
                !redisUtil.hasKey(key) ||
                !redisUtil.get(key).toString().equals(currentTimeMillis)) {
            throw new AuthenticationException("Token失效，请重新登录!");
        }
        return sysUser;
    }

    /**
     * 支持JwtToken代替shiro的Token
     */
    @Override
    public boolean supports(AuthenticationToken token) {
        return token instanceof JwtToken;
    }

    /**
     * 清除当前用户的权限认证缓存
     *
     * @param principals 权限信息
     */
    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }


}
